#!/usr/bin/python
'''
Script to iterate through all nodes and install Java on each via scp and ssh.
'''

import os
import nodes, ssh, ping
import cPickle
from Queue import Queue
from threading import Thread
from time import sleep

def deploy_single(hostname, password, command, src_file, 
                   dst_file='/home/usf_ubcslice5/',
                   username='usf_ubcslice5',
                   keyfile='id_rsa' 
                   ):
    'Single-node deployment.'
    # test for SSH
    if not ping.has_ssh(hostname, timeout=7.5): 
        print 'Node doesn''t have port 22 open. No SSH?'
        return (False,"no ssh")
    # upload
    if src_file:
        #print 'Uploading...'
        ok = ssh.upload(src_file, dst_file, password, username, keyfile, 
                                                                hostname)
        if ok: 
            #print '[PASS]' 
            pass
        else: 
            #print '[FAIL]'
            #print 'Moving on to the next node!'
            return (False,"upload fail")
    # run command
    #print 'Executing Command...',
    ok = ssh.command(command, password, username, keyfile, hostname)
    if ok: 
        #print '[PASS]'
        return (True,ok)
    else: 
        #print '[FAIL]'
        return (False,"cmd fail")

NUM_WORKERS = 75 # What is the optimum value?
def deploy(password, command, src_file, 
           dst_file='/home/usf_ubcslice5/',
           username='usf_ubcslice5',
           keyfile='id_rsa',
           max_time=0.0, # max seconds to wait for completion
           hostnames=[], # overriding list of hostnames to deploy to
           ):
    'Parallelized mass-deploy to our planetlab nodes.'

    # Cached get of node hostnames.
    hosts = []
    if not hostnames:
        if os.path.exists('deploy_hosts.cache'):
            hosts = cPickle.load(open('deploy_hosts.cache', 'rb'))
        else:
            print 'Getting lists of hostnames...'
            hosts = nodes.get_hostnames()
            cPickle.dump(hosts, open('deploy_hosts.cache', 'wb'))
    else:
        hosts = hostnames

    hosts = hosts[:len(hosts)/2] # TEMPORARY, REMOVE REMOVE REMOVE!
    print 'Deploying to',len(hosts),' nodes.'

    # Prepare threadsafe list of tasks.
    queue = Queue(0) # unlimited size
    for number,hostname in enumerate(hosts): queue.put((number,hostname))

    # Spawn workers.
    output = Queue(0) 
    for i in range(NUM_WORKERS):
        w = Thread(target=worker, args=(queue, output, password, command,
                                        src_file, dst_file, username, keyfile))
        w.setDaemon(True)
        w.start()

    # Block until all tasks are used up and finished via task_done()
    if max_time==0.0: 
        queue.join()
    else:
        while (queue.qsize()>0): sleep(1.0)
        sleep(max_time)

    summary = {'success':0,'upload fail':0,'cmd fail':0,'no ssh':0}
    nodess = {'success':{},'upload fail':[],'cmd fail':[],'no ssh':[]}
    while not output.empty(): # I don't think queues are iterable :(
        hostname,ret = output.get()
        ok,text = ret
        if ok:
            summary['success']+=1
            nodess['success'][hostname]=text
        else:
            summary[text]+=1
            nodess[text].append(hostname)
        
    return summary,nodess

def worker(in_q, out_q, password, command, src_file, dst_file, username, keyfile):
    'Worker daemon thread, deploying to nodes in in_q.'
    while 1:
        num,hostname = in_q.get() # blocks indefinitely on empty
        print '\tDeploying to node: %s (#%d)' % (hostname,num)
        result = deploy_single(hostname, password, command, 
                               src_file, dst_file, username, keyfile)
        out_q.put((hostname, result))
        print '\tDone deploying to node: %s (#%d)' % (hostname,num)
        in_q.task_done() # beautiful! a way to signal task completion!
    
INSTALL_JAVA='tar -xvzf jre.tar.gz;\
sudo ln -s /home/usf_ubcslice5/jre1.6.0_16/bin/java /usr/bin/java'
#echo PATH=/home/usf_ubcslice5/jre1.6.0_16/bin:$PATH >> .bash_profile;\
#echo export PATH >> .bash_profile'

JSTOP='sudo killall java'
JSTART='killall java;java -jar Client.jar 24.86.30.146:8000 20'

CLEAN_MONITORD='rm monitord-*.tgz;\
sudo killall monitord.py;\
sudo rm /var/cache/monitord.neighbours;\
sudo rm -rf /usr/local/monitord;'

INSTALL_MONITORD='tar -xvzf monitord-1.0.tgz;\
sudo killall monitord.py;\
sudo rm -rf /usr/local/monitord;\
sudo touch /var/cache/monitord.neighbours;\
sudo chmod 666 /var/cache/monitord.neighbours;\
sudo mv monitord /usr/local;\
sudo /usr/local/monitord/monitord.py'

CLEAN_CRAWLD='rm crawld.tgz;\
sudo killall crawld.py;\
sudo rm /var/cache/crawld.cache;\
sudo rm -rf /usr/local/crawld;'

INSTALL_CRAWLD='tar -xvzf crawld.tgz;\
sudo killall crawld.py;\
chmod 755 crawld.py;\
./crawld.py'


INSTALL_CRAWLD_OLD='tar -xvzf crawld.tgz;\
sudo killall crawld.py;\
sudo rm -rf /usr/local/crawld;\
sudo touch /var/cache/crawld.cache;\
sudo chmod 666 /var/cache/crawld.cache;\
sudo mv crawld /usr/local;\
sudo /usr/local/crawld/crawld.py'


LS='ls'

PASSWORD = 'eece411'
SRC_FILE = 'monitord-1.0.tgz'#'jre.tar.gz'
def jtest():
    return deploy_single('planetlab2.fri.uni-lj.si', PASSWORD,
                        INSTALL_JAVA, None)

def check_java(): # ugly hack to achieve current ends
    sum,nodess = deploy(PASSWORD,
          "java -version",
          None, # source file
          max_time=30.0)
    print 'Summary:',sum
    results = {}
    nodess = nodess['success']
    for k in nodess:
        s = nodess[k]
        i =  s.find('version')
        if i==-1: continue
        s = s[i+9:] # cut off everything before version num
        i = s.find('"')
        s = s[:i] # cut off everything after
        if s in results:
            results[s].append(k)
        else:
            results[s] = [k]
    for k in results:
        print '\t',k,'installed on',len(results[k]),'nodes'
    return results
    
def install():
    return deploy(PASSWORD,
                  INSTALL_MONITORD,   # command
                  SRC_FILE, max_time=60.0) # source file
def remove():
    return deploy(PASSWORD,
                  CLEAN_MONITORD,   # command
                  None, max_time=60.0) # source file

def cmd(cmd):
    return deploy(PASSWORD, cmd, None, max_time=30.0)
